package cn.com.dhcc.app.pub.core.service;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;

import javax.sql.DataSource;

import org.apache.ftpserver.ftplet.Authentication;
import org.apache.ftpserver.ftplet.AuthenticationFailedException;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.User;
import org.apache.ftpserver.usermanager.AnonymousAuthentication;
import org.apache.ftpserver.usermanager.PasswordEncryptor;
import org.apache.ftpserver.usermanager.UsernamePasswordAuthentication;
import org.apache.ftpserver.usermanager.impl.DbUserManager;
import org.apache.ftpserver.usermanager.impl.UserMetadata;
import org.apache.ftpserver.util.StringUtils;
import org.apache.log4j.Logger;

import cn.com.dhcc.app.core.service.Log;
import cn.com.dhcc.app.core.service.Log.LOGTYPE;
import cn.com.dhcc.app.core.util.StrUtil;
import cn.com.dhcc.turbo.service.exch.ApiUserAuthService;
import cn.com.dhcc.turbo.service.exch.FtpApiUserInfo;

/**
 * 自定义的dbusermanager，添加授权码的验证 
 * @author qiao
 * @2015-1-7 @下午4:00:44
 */
public class TurboDbUserManager extends DbUserManager {

	private ApiUserAuthService apiUserAuthService;
	@SuppressWarnings("deprecation")
	private final Logger logger = Log.getDetachLogger(LOGTYPE.TURBO);
	
	public TurboDbUserManager(DataSource dataSource, String selectAllStmt,
			String selectUserStmt, String insertUserStmt,
			String updateUserStmt, String deleteUserStmt,
			String authenticateStmt, String isAdminStmt,
			PasswordEncryptor passwordEncryptor, String adminName) {
		super(dataSource, selectAllStmt, selectUserStmt, insertUserStmt,
				updateUserStmt, deleteUserStmt, authenticateStmt, isAdminStmt,
				passwordEncryptor, adminName);
	}
	public TurboDbUserManager(DataSource dataSource, String selectAllStmt,
			String selectUserStmt, String insertUserStmt,
			String updateUserStmt, String deleteUserStmt,
			String authenticateStmt, String isAdminStmt,
			PasswordEncryptor passwordEncryptor, String adminName,ApiUserAuthService apiUserAuthService) {
		super(dataSource, selectAllStmt, selectUserStmt, insertUserStmt,
				updateUserStmt, deleteUserStmt, authenticateStmt, isAdminStmt,
				passwordEncryptor, adminName);
		this.apiUserAuthService = apiUserAuthService;
	}
	@Override
	public User authenticate(Authentication authentication)
			throws AuthenticationFailedException {
		 if (authentication instanceof UsernamePasswordAuthentication) {
	            UsernamePasswordAuthentication upauth = (UsernamePasswordAuthentication) authentication;
	            UserMetadata userMetadata  = upauth.getUserMetadata();
	            String user = upauth.getUsername();
	            String password = getRealPasswd(upauth.getPassword());
	            String adapterCode = getAdapterCode(upauth.getPassword());
	            if (StrUtil.isBlank(user)||StrUtil.isBlank(password)) {
	                throw new AuthenticationFailedException("Authentication failed");
	            }
	            
	            String authCode = getAuthCode(upauth.getPassword());
	            FtpApiUserInfo ftpApiUserInfo = new FtpApiUserInfo();
	            ftpApiUserInfo.setAdapterCode(adapterCode);
	            ftpApiUserInfo.setAuthCode(authCode);
	            ftpApiUserInfo.setUserMetadata(userMetadata);
	            
	            if(!apiUserAuthService.isAdapterAuthCodeOkForFtp(ftpApiUserInfo)){
	            	throw new AuthenticationFailedException("Authentication failed");
	            }
	            if (password == null) {
	                password = "";
	            }

	            Statement stmt = null;
	            ResultSet rs = null;
	            try {

	                // create the sql query
	                HashMap<String, Object> map = new HashMap<String, Object>();
	                map.put(ATTR_LOGIN, escapeString(user));
	                String sql = StringUtils.replaceString(getSqlUserAuthenticate(), map);
	                logger.info(sql);

	                // execute query
	                stmt = createConnection().createStatement();
	                rs = stmt.executeQuery(sql);
	                if (rs.next()) {
	                    try {
	                        String storedPassword = rs.getString(ATTR_PASSWORD);
	                        if (getPasswordEncryptor().matches(password, storedPassword)) {
	                            return getUserByName(user);
	                        } else {
	                            throw new AuthenticationFailedException(
	                                    "Authentication failed");
	                        }
	                    } catch (FtpException e) {
	                        throw new AuthenticationFailedException(
	                                "Authentication failed", e);
	                    }
	                } else {
	                    throw new AuthenticationFailedException(
	                            "Authentication failed");
	                }
	            } catch (SQLException ex) {
	            	logger.error("DbUserManager.authenticate()", ex);
	                throw new AuthenticationFailedException(
	                        "Authentication failed", ex);
	            } finally {
	                closeQuitely(rs);
	                closeQuitely(stmt);
	            }
	        } else if (authentication instanceof AnonymousAuthentication) {
	            try {
	                if (doesExist("anonymous")) {
	                    return getUserByName("anonymous");
	                } else {
	                    throw new AuthenticationFailedException(
	                            "Authentication failed");
	                }
	            } catch (AuthenticationFailedException e) {
	                throw e;
	            } catch (FtpException e) {
	                throw new AuthenticationFailedException(
	                        "Authentication failed", e);
	            }
	        } else {
	            throw new IllegalArgumentException(
	                    "Authentication not supported by this user manager");
	        }
	}
	private String getAuthCode(String pwd) {
		    if(StrUtil.isBlank(pwd))return null;
			String[] pwd_authcode = pwd.split("\\s+");
			if(pwd_authcode.length<3)return null;
			String authCode = pwd_authcode[2];
			return authCode;
	}
	private String getAdapterCode(String pwd) {
	    if(StrUtil.isBlank(pwd))return null;
		String[] pwd_authcode = pwd.split("\\s+");
		if(pwd_authcode.length<3)return null;
		String authCode = pwd_authcode[1];
		return authCode;
    }
	 private String getRealPasswd(String pwd){
		 try {
			 if(StrUtil.isBlank(pwd))return null;
				String[] pwd_authcode = pwd.split("\\s+");
				if(pwd_authcode.length<3)return null;
				String realPwd = pwd_authcode[0];
				return realPwd;
		} catch (Exception e) {
			logger.error("解析ftp登陆授权码错误",e);
			return null;
		}
		 
	 }
	private void closeQuitely(Statement stmt) {
	        if(stmt != null) {
		    Connection con = null;
		    try {
			con = stmt.getConnection();
		    } catch (Exception e) {
		    }
		    try {
	                stmt.close();
	            } catch (SQLException e) {
	                // ignore
	            }
		    closeQuitely(con);
	        }
	    }
	 private void closeQuitely(ResultSet rs) {
	        if(rs != null) {
	            try {
	                rs.close();
	            } catch (SQLException e) {
	                // ignore
	            }
	        }
	    }

	    protected void closeQuitely(Connection con) {
		if (con != null) {
		    try {
			con.close();
		    } catch (SQLException e) {
			// ignore
		    }
		}
	    }
	    /**
	     * Escape string to be embedded in SQL statement.
	     */
	    private String escapeString(String input) {
	        if (input == null) {
	            return input;
	        }

	        StringBuilder valBuf = new StringBuilder(input);
	        for (int i = 0; i < valBuf.length(); i++) {
	            char ch = valBuf.charAt(i);
	            if (ch == '\'' || ch == '\\' || ch == '$' || ch == '^' || ch == '['
	                    || ch == ']' || ch == '{' || ch == '}') {

	                valBuf.insert(i, '\\');
	                i++;
	            }
	        }
	        return valBuf.toString();
	    }
}
